/*
 * CS 567 GUI - Semester Project - "DecisionLists"
 * Jason Frank
 * Spring 2011
 *
 * This File: Panel_Fader3.  This class handles fading from one JPanel to another Jpanel (Strategy Pattern).
 *            A client will instantiate this class, providing the target JPanel on which the 2 panels should
 *            be placed (for the crossfade).  Note that the crossfade is accomplished via drawing a background-
 *            colored rectangle over each panel, with varying opacity.  Thus, the client should be aware
 *            that this object will need to add itself (a JPanel) to the target_panel.  The client should
 *            not do this themselves.
 */

package Decision_Lists;

import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.Timer;

/*
 */
public class Panel_Fader3 extends JPanel {

  JPanel target_panel, fadeout_panel, current_panel, fadein_panel;
  boolean test_fade = true;
  Graphics g;  // the caller's Graphics obj
  ////////////////////////////////////////////
  ///// FADE MEMBERS /////////////////////////
  private float fade_alpha;  // used in paintComp (fade handlers set it as fadout_alpha or fadein_alpha)

  //private int   fadeout_interval = 19;  // miliseconds between calls to the action listener.
  private int   fadeout_interval = 10;  // miliseconds between calls to the action listener.
  private int   fadeout_init_delay = 0;
  private Timer fadeout_timer = new Timer(fadeout_interval, null);  // handler func gets set in constructor
  private float fadeout_alpha = 1.0f;  // start opacity at 1.0 (fully opaque)
  //private float fadeout_delta = 0.06f;
  private float fadeout_delta = 0.08f; // 4/26/11 to increase performance/decrease lag

  private int   fadein_interval = fadeout_interval;  // miliseconds between calls to the action listener.
  private int   fadein_init_delay = fadeout_init_delay;
  private Timer fadein_timer = new Timer(fadein_interval, null);

  private float fadein_alpha = 0f;  // start opacity at 0.0  (fully transparent)
  //private float fadein_delta = 0.06f;
  private float fadein_delta = 0.08f; // 4/26/11 to increase performance/decrease lag

  /////////// END OBJECT VARS ///////////////////
  ///////////////////////////////////////////////


  // Constructor
  /*
   * Params: taget_panel is the JPanel that you wish the fadeout/fadein panels to be displayed in
   *         (this obj will add/remove them to/from that panel).
   */
  public Panel_Fader3(JPanel target_panel, JPanel current_panel) throws Exception  {
    // Initialize class vars
    if( target_panel == null ) {  // the thrown exception below also exits the program
      throw new Exception("Panel_Fader constructor found that target_panel was null.");
    }
    this.target_panel  = target_panel;
    this.current_panel = this.fadeout_panel = current_panel;

    // setLayout seems to be needed to match the target_panel's layout situation
    //  (or at least to match how the original Center_Panel / Panel_Fader combo worked)
    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    add(this.current_panel);
    // Must add this JPanel (self) to the target_panel so that the fadeout or fadein panels show up somewhere
    this.target_panel.add(this);

    // Set the FADE EVENT HANDLERs
    fade_alpha = fadeout_alpha;  // initialize the generic alpha that paintComponent uses.
    fadeout_timer.addActionListener(fadeout_handler   );
    fadeout_timer.setInitialDelay(  fadeout_init_delay);
    fadein_timer.addActionListener( fadein_handler    );
    fadein_timer.setInitialDelay(   fadein_init_delay );

  } ////// end one constructor //////

 
  public void startFade() throws Exception {

    // If this obj is for a "crossfade"
    if(fadeout_panel != null && fadein_panel != null ) {

      reset_alphas();  // reset the alphas to their starting values (reset from prev-fade's left-off point)
      fadeout_timer.start();  // calls fadeout_handler() at specified intervals
    }
    // Catch cases where one or both of the panels is null
    else if(fadeout_panel == null && fadein_panel == null ) {
      throw new Exception("Panel_Fader3::startFade(): Both \"fadeout_panel\" and \"fadein_panel\""+
                         " are null.  Cannot proceed.");
    }
    else if(fadeout_panel == null ) {
      throw new Exception("Panel_Fader3::startFade(): \"fadeout_panel\" is null; cannot proceed.");
    }
    else /* if fadin_panel == null */ {
      throw new Exception("Panel_Fader3::startFade(): \"fadein_panel\" is null; cannot proceed.");
    }

  } ////// end startFade() ////////////////////

  
  // FADE OUT handler
  ActionListener fadeout_handler = new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {

      fadeout_alpha -= fadeout_delta;
      // Stop the timer if the panel is fully faded out.
      if(fadeout_alpha < 0) {
        fadeout_alpha = 0;  // the AlphaComposite requires alpha to be [0.0,1.0]
        fade_alpha =  fadeout_alpha;  // set the generic alpha that paintComponent uses.
        fadeout_timer.stop();
        //System.err.println("fadeout_handler about to remove panel: " + fadeout_panel.getName());
        remove(fadeout_panel);
//        System.err.println("\n\nfadeout_handler just removed panel: " +
//                            ((DL_ViewInterface)fadeout_panel).getViewName());
        add(fadein_panel);
//        System.err.println("\n\nfadeout_handler just ADDED panel: " +
//                            ((DL_ViewInterface)fadein_panel).getViewName());
        fadein_timer.start();
      }///
      // The paintComponent of target_panel then calls paint_caller_panel(), which draws the overlay rect
                //      target_panel.validate();
                //      target_panel.revalidate();
                //      target_panel.invalidate();
                //      target_panel.getParent().validate();
                //      target_panel.getParent().repaint();
                //      target_panel.getParent().getParent().validate();
                //      target_panel.getParent().getParent().repaint();

      target_panel.repaint();  // so panel can call it's super.paintComponent()
            //target_panel.paintComponent(target_panel.getGraphics());
            //target_panel.paintComponents(target_panel.getGraphics());

      fade_alpha =  fadeout_alpha;  // set the generic alpha that paintComponent uses.
      if(test_fade) System.out.println("fadeout_handler just set fade_alpha to: " + fade_alpha);
    }
  }; ///// end FADE OUT handler ////

  // FADE IN handler
  ActionListener fadein_handler = new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {

      fadein_alpha += fadein_delta;
      // Stop the timer if the panel is fully faded out.
      if(fadein_alpha > 1.0 ) {
        fadein_alpha = 1.0f;  // the AlphaComposite requires alpha to be [0.0,1.0]
        fade_alpha =  fadein_alpha;  // set the generic alpha that paintComponent uses.
        fadein_timer.stop();

        // Make fadin_panel the fadeout_panel & the current_panel, since fading
        //  is complete (sets things up for next fade)
        current_panel = fadeout_panel = fadein_panel;
      }///
      // Make the container lay out its subcomponents again (fills up the window appropriately
      //                              even if user resized window when looking at a diff view)
      target_panel.validate();
      // The paintComponent of target_panel then calls paint_caller_panel(), which draws the overlay rect
      target_panel.repaint();  // so panel can call it's super.paintComponent()


      fade_alpha =  fadein_alpha;  // set the generic alpha that paintComponent uses.
      if(test_fade) System.out.println("fadein_handler just set fade_alpha to: " + fade_alpha);

    } /////////// end actionPerformed() (for the fadein_handler) //////
  };  ///// end FADE IN handler ////

   // <editor-fold defaultstate="collapsed" desc="Could not get same functionality by using paintComponent in this class">
  /*
  @Override
  public void paintComponent(Graphics g) {
  super.paintComponent(g);

  //pf.paint_caller_panel(g);
  //    paint_caller_panel(g);

  // MUST USE THE ORIGINAL G to make the fade work!
  Graphics2D g2 = (Graphics2D) g; // 2D needed for composite information
  /////////////////////////////////////////////////////////////////////////
  ///// SETUP "COMPOSITE" TO ENABLE ALPHA FADING (by timer & handler) /////

  // tutorial at http://java.sun.com/products/jfc/tsc/articles/swing2d/
  // Save orig composite? NO! - DO NOT RESTORE THE OLD COMPOSITE (to make the fade work!)

  // Create an AlphaComposite
  Composite fadeout_composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fade_alpha);
  // Set the composite on the Graphics2D object.
  g2.setComposite(fadeout_composite);

  if(test_fade) System.err.println("paintComponent() just did setComposit() with fade_alpha: " + fade_alpha);

  g2.setPaint(target_panel.getBackground()); // draw on top with same color as background
  g2.fillRect(0, 0, target_panel.getWidth(), target_panel.getHeight());

  // Restore the orig composite? NO! - DO NOT RESTORE THE OLD COMPOSITE (to make the fade work!)

  } //// end paintComponent() ///////
   */// </editor-fold>
  
  // emulating paintComponent in the calling panel (uses the panel's graphics obj)
  //public void paint_caller_panel() {
  public void paint_caller_panel(Graphics g) {

    // MUST USE THE ORIGINAL G to make the fade work!
    Graphics2D g2 = (Graphics2D) g; // 2D needed for composite information
    /////////////////////////////////////////////////////////////////////////
    ///// SETUP "COMPOSITE" TO ENABLE ALPHA FADING (by timer & handler) /////

            // tutorial at http://java.sun.com/products/jfc/tsc/articles/swing2d/
            // Save orig composite? NO! - DO NOT RESTORE THE OLD COMPOSITE (to make the fade work!)
    // Create an AlphaComposite
    Composite fadeout_composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fade_alpha);
    // Set the composite on the Graphics2D object.
    g2.setComposite(fadeout_composite);
    if(test_fade) System.err.println("paint_caller_panel just did setComposit() with fade_alpha: " + fade_alpha);

    g2.setPaint(target_panel.getBackground()); // draw on top with same color as background
    g2.fillRect(0, 0, target_panel.getWidth(), target_panel.getHeight());
            // Restore the orig composite? NO! - DO NOT RESTORE THE OLD COMPOSITE (to make the fade work!)
  } //// end paint_caller_panel() ///////

  private void reset_alphas() {
      // Resets
    fadein_alpha = 0.0f;
    fadeout_alpha = 1.0f;
  }

  ////////////////////////////////////////////////////////
  //////// GETTERS AND SETTERS ///////////////////////////
  public void setFadein_panel(JPanel fadein_panel) {
    this.fadein_panel = fadein_panel;
  }

  public void setFadeout_panel(JPanel fadeout_panel) {
    this.fadeout_panel = fadeout_panel;
  }

  public JPanel getCurrent_panel() {    return current_panel;  }

  public float getFadein_alpha() {    return fadein_alpha;  }

  public void setFadein_alpha(float fadein_alpha) {
    if(fadein_alpha < 0f  || fadein_alpha > 1.0f ) {
      System.err.print("fade alphas must be [0.0,1.0]");
      System.exit(-1);
    }
    this.fadein_alpha = fadein_alpha;
  }

  public float getFadein_delta() {    return fadein_delta;  }

  public void setFadein_delta(float fadein_delta) {
    if(fadein_delta < 0f  || fadein_delta > 1.0f ) {
      System.err.print("fadeout_delta must be [0.0,1.0]");
      System.exit(-1);
    }
    this.fadein_delta = fadein_delta;
  }

  public int getFadein_init_delay() {      return fadein_init_delay;  }

  public void setFadein_init_delay(int fadein_init_delay) {
    this.fadein_init_delay = fadein_init_delay;
  }

  public int getFadein_interval_miliseconds() {    return fadein_interval;  }

  public void setFadein_interval_miliseconds(int fadein_interval) {
    this.fadein_interval = fadein_interval;
    fadein_timer.setDelay(fadein_interval);
  }

  public float getFadeout_alpha() {    return fadeout_alpha;  }

  public void setFadeout_alpha(float fadeout_alpha) {
    if(fadeout_alpha < 0f  || fadeout_alpha > 1.0f ) {
      System.err.print("fade alphas must be [0.0,1.0]");
      System.exit(-1);
    }
    this.fadeout_alpha = fadeout_alpha;
  }

  public float getFadeout_delta() {    return fadeout_delta;  }

  public void setFadeout_delta(float fadeout_delta) {
    if(fadeout_delta < 0f  || fadeout_delta > 1.0f ) {
      System.err.print("fadeout_delta must be [0.0,1.0]");
      System.exit(-1);
    }
    this.fadeout_delta = fadeout_delta;
  }

  public int getFadeout_init_delay() {    return fadeout_init_delay;  }

  public void setFadeout_init_delay(int fadeout_init_delay) {
    this.fadeout_init_delay = fadeout_init_delay;
  }

  public int getFadeout_interval_miliseconds() {   return fadeout_interval;  }

  public void setFadeout_interval_miliseconds(int fadeout_interval) {
    this.fadeout_interval = fadeout_interval;
    fadeout_timer.setDelay(fadeout_interval);
  }


  public float getFade_alpha() {    return fade_alpha;  }

  public JPanel getTarget_panel() {    return target_panel;  }

  //////// end getters and setters //////////////
  //////////////////////////////////////////////
  /*
  @Override
  public Dimension getMinimumSize()   {
    if(target_panel.getMinimumSize() != null)
      return target_panel.getMinimumSize();
    else
      return new Dimension(target_panel.getWidth(), target_panel.getHeight());
  }
  
  @Override  
  public Dimension getPreferredSize() { 
    if(target_panel.getPreferredSize() != null)
      return target_panel.getPreferredSize();
    else
      return new Dimension(target_panel.getWidth(), target_panel.getHeight());
  }
  */

  /*
  @Override
  public Dimension getMaximumSize() {
    if (target_panel.getMaximumSize() !=null)
      return target_panel.getMaximumSize();
    else if(target_panel.getPreferredSize() !=null)
      return target_panel.getPreferredSize();
    else
      return new Dimension(target_panel.getWidth(), target_panel.getHeight());
  }
   */

} //////////////////// end class Panel_Fader /////////////////////////////
